-
Notifications
You must be signed in to change notification settings - Fork 799
feat(server): support json schema to define tool parameters #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(server): support json schema to define tool parameters #423
Conversation
d85a794
to
2ff2e88
Compare
2ff2e88
to
2f85e25
Compare
@@ -130,3 +130,5 @@ out | |||
|
|||
.DS_Store | |||
dist/ | |||
|
|||
.vscode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to the PR, but I had to stop my vscode from trying to format files with Prettier and removing random trailing whitespace.
Happy to remove that change and keep it for a separate PR!
This is a pretty important feature for a project I am working on that allows MCP aggregation. Been spinning my wheels for days, only to realize its zod only. I am also trying to hack it by converting openapi to zod, and its not going pretty. |
|
||
const isJsonSchemaObject = (obj: unknown): obj is ParametersJsonSchema => { | ||
return Boolean( | ||
obj && typeof obj === "object" && "type" in obj && typeof obj.type === "string" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since type
is not a required keyword for a JSON schema and it would be more robust not to ever infer a JSON schema version, can we just look for $schema
instead? (Note that I am an interested bystander, not an approver/maintainer, so take this suggestion with the requisite grain of salt.)
## 🤔 What - **feat** introduce CustomMcpServer class that handles JSONSchema directly ## 🤷 Why The official SDK requires us to define tools' input with Zod, which causes a few problems: - We have to generate these Zod schemas dynamically from json schema, which is not always easy to do accurately especially when you have recursive schemas. You can checkout [the readme of this lib](https://www.npmjs.com/package/json-schema-to-zod) for example which highlights that recursivity is only partially supported, and generating schemas at runtime only works by using eval 😬 . - Related to the above, enabling some tools would crash the server because of a bad recursion when generating the zod schema 🙈 - Moreover, this Zod schema is then transformed back to a json schema anyway before being sent to the mcp client 🙃 I opened [a PR](modelcontextprotocol/typescript-sdk#423) on the official SDK repository, but I'm not sure if or when it will be merged (it's already full of conflicts because of refactorings happening on the main branch). So I decided to do it in user land instead! ## 🔍 How I'm leveraging the lower level `Server` class to implement my own `McpServer`. I mirrored the base implementation except I modified the `inputSchema` to accept a json schema instead. Tool arguments are now validated against this schema by using [`ajv`](https://www.npmjs.com/package/ajv). I also had to modify the base openapi specs because the way it combined `allOf` and `additionalProperties: false` resulted in the validation rejecting actually valid inputs. I had to leverage [`unevaluatedProperties`](https://json-schema.org/understanding-json-schema/reference/object#unevaluatedproperties) instead. I'll work on backporting these changes to the specs source of truth in https://github.com/algolia/api-clients-automation/tree/main/specs ## 🧪 Testing I added a ton of integration tests around tool arguments validation I have a test that makes sure that non of the tools can crash the server I also tested manually with Claude desktop that calls are still working
Adds the ability to define tool parameters in json schema directly
Motivation and Context
Fixes #283
The
typescript-sdk
uses Zod to validate the parameters passed to tools and generates a json schema representation from it for the MCP client. There are cases where you might want to define tools directly with a JSON Schema: for example, when generating mcp tools dynamically from an OpenAPI Specification (example here). In these cases, you have to go from json schema, to Zod and back to json schema which is wasteful and hard to do without losing information along the way (for example, when you have recursive types).Example usage
How Has This Been Tested?
Breaking Changes
None, the changes are intended to only be additive.
Types of changes
Checklist
Additional context
args
(which is just marked asobject
).MCPServer#tool()
which accepts a configuration object instead of positional arguments. I only did that because I struggled to make the types work with positional arguments. Notably, I was losing type inference with theToolCallback
conditional type. Typescript does seem to infer types correctly when using the object version though. And while I understand it's completely subjective, I think having an object version makes for a nicer API (without removing previous options) and it would address Allow passing a Tool object to the server.tool() method #369mcp.ts
to keep the methods from growing too much from the additional complexity